#!/usr/bin/python
# -*- coding: utf-8 -*-


import BasicHTTPServiceAPI.BasicHTTPService
import oauth

from error import APIError
from utils import convert_to_utf8_str


def bind_api(**config):
    """function for binding api method"""
    class APIMethod(object):
        """class for representing a api method"""
        url = config.get('url')
        method = config.get('method', 'GET')
        allowed_params = config.get('allowed_params', [])
        additional_params = config.get('additional_params', None)
        require_auth = config.get('require_auth', False)
        SSL = config.get('ssl', False)
        data_type = config.get('data_type', None)
        data_format = config.get('data_format', 'json')
        data_list = config.get('data_list', False)
        multipart_form = config.get('multipart_form', False)

        def __init__(self, api, args, kargs):
            """constructor for APIMethod"""
            self.api = api
            self.params = self.build_params(args, kargs)
            tempbaseUrl = ''
            if self.SSL:
                tempbaseUrl += 'https://'
            else:
                tempbaseUrl += 'http://'
            self.baseUrl = tempbaseUrl + self.api.host + self.url
            if self.require_auth:
                self.params.update(self.build_oauth())
                self.sign_request()
            if self.additional_params:
                self.params.update(self.build_additional_params(args, kargs))

        def build_params(self, args, kargs):
            """build params for api method"""
            params = {}
            for idx, value in enumerate(args):
                try:
                    if type(value) is dict:
                        params.update(value)
                    else:
                        params[self.allowed_params[idx]] = convert_to_utf8_str(value)
                except IndexError:
                    raise APIError('Too many parameters supplied!')
            for k, v in kargs.items():
                if v is None:
                    continue
                if k in params:
                    raise APIError('multiple parameters for %s are supplied!' % k)
                if k in self.allowed_params:
                    params[k] = convert_to_utf8_str(v)
            return params

        def build_additional_params(self, args, kargs):
            """build additional params which is not included in the signature string"""
            params = {}
            for k, v in kargs.items():
                if v is None:
                    continue
                if k in params:
                    raise APIError('multiple parameters for %s are supplied!' % k)
                if k in self.additional_params:
                    params[k] = open(v, 'rb')  # additional_params is a filename
            return params


        def build_oauth(self):
            """build oauth params for api method"""
            oauth_params = {}
            oauth_params['oauth_consumer_key'] = convert_to_utf8_str(self.api.consumer.key)
            oauth_params['oauth_nonce'] = convert_to_utf8_str(oauth.generate_nonce(32))
            oauth_params['oauth_timestamp'] = convert_to_utf8_str(oauth.generate_timestamp())
            if self.api.access_token:
                oauth_params['oauth_token'] = convert_to_utf8_str(self.api.access_token.key)
            oauth_params['oauth_version'] = convert_to_utf8_str('1.0')
            return oauth_params

        def sign_request(self):
            """sign the request with HMAC-SHA1"""
            tempRequest = oauth.OAuthRequest(self.method, self.baseUrl, \
                                            self.params)
            signMethod = oauth.OAuthSignatureMethod_HMAC_SHA1()
            tempRequest.sign_request(signMethod, self.api.consumer, \
                                    self.api.access_token)
            self.params = tempRequest.parameters

        def execute(self):
            """execute the api method"""
            res = BasicHTTPServiceAPI.BasicHTTPService.basicAPIFetch( \
                                                 self.api.host, \
                                                 self.url, \
                                                 self.params, \
                                                 method=self.method,\
                                                 SSL=self.SSL, \
                                                 multipart=self.multipart_form)
            if res['status'] < '200':
                return self.api.parser.parse(self, res['data'])
            else:
                raise APIError(res['status'])

    def _call(api, *args, **kargs):
        """call api method"""
        method = APIMethod(api, args, kargs)
        return method.execute()

    return _call
